import java.awt.Dimension;
import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Calendar;
import java.util.HashSet;

import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;


public class Main {
	static LinkerSymbolTable SYMBOL_TABLE;
	static LinkerIntermediate OUTPUT_INTERMEDIATE;
	static LinkerIntermediate LOADER_INTERMEDIATE;
	static LinkerLine currentline;
	static LinkerLine outputLine;
	static ErrorHandler eHandler;
	static File inputfile;
	static File outputdir;
	static String PROGRAM_NAME;
	static String LOAD_PROGRAM_NAME;
	static String previous_intermediate_string;
	static int CURRENT_MODULE_LENGTH;
	static int totalrecords;
	static int t_record_count;
	static int l_record_count;
	static int m_record_count;
	static int loader_total_count;
	static int loader_t_count;
	static int execution_start;
	static boolean valid_header;
	static boolean first_module;
	static boolean modify;
	static boolean add_for_linking;
	static boolean valid_first_header;
	static boolean continue_linking;
	static String adjustments;
	static int address_adjustment;
	static int last_assigned_linker_address;
	static int previous_module_length;
	static String first_module_load_point;
	static String first_module_execution;
	static String modified_address_hex;
	static String modified_value;
	private final static String newline = System.getProperty("line.separator");

	/**
	 * @param args
	 */
	 public static void main(String[] args) {
		 createFrame();

		 eHandler = new ErrorHandler(); 	
	 }

	 private static void createFrame() {
		 try {
			 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		 } catch (ClassNotFoundException e) {
			 e.printStackTrace();
		 } catch (InstantiationException e) {
			 e.printStackTrace();
		 } catch (IllegalAccessException e) {
			 e.printStackTrace();
		 } catch (UnsupportedLookAndFeelException e) {
			 e.printStackTrace();
		 }

		 JFrame frame = new JFrame("Group 02");
		 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		 frame.add(new AssemblerFrame());
		 frame.setMinimumSize(new Dimension(400, 700));
		 frame.setVisible(true);    
		 frame.setSize((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth()/2, (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight()/2);
		 frame.setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth()/4, (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight()/10);	
	 }

	 public static void run(File selectedFile) {
		 OUTPUT_INTERMEDIATE = new LinkerIntermediate();
		 LOADER_INTERMEDIATE = new LinkerIntermediate();
		 SYMBOL_TABLE = new LinkerSymbolTable();
		 outputLine = new LinkerLine();
		 PROGRAM_NAME = "";
		 LOAD_PROGRAM_NAME = "";
		 CURRENT_MODULE_LENGTH = 0;
		 previous_intermediate_string = "";
		 totalrecords = 0;
		 t_record_count = 0;
		 l_record_count = 0;
		 m_record_count = 0;
		 loader_total_count = 0;
		 loader_t_count = 0;
		 execution_start = 0;
		 valid_header = false;
		 first_module = true;
		 valid_first_header = false;
		 continue_linking = true;
		 modify = false;
		 add_for_linking = true;
		 adjustments = "";
		 address_adjustment = 0;
		 last_assigned_linker_address = 0;
		 previous_module_length = 0;

		 runLinkerPassOne(selectedFile);
		 if (continue_linking) {
			 runLinkerPassTwo();
		 }
		 else {
			 // Print Fatal Error
			 for(LinkerLine l : OUTPUT_INTERMEDIATE){
				 outputPrint(l.toString());
				 for (int i : l.getErrors()) {
					 outputPrint("ERROR " + i + ": " + eHandler.get(i));
				 }
			 }
			 lfilePrint("**LINKING/LOADING HAS BEEN STOPPED DUE TO FATAL ERROR! NO LOADER FILE HAS BEEN CREATED**");
		 }
	 }

	 private static void runLinkerPassOne (File selectedFile) {

		 try {
			 BufferedReader in = new BufferedReader(new FileReader(selectedFile));
			 String str = null;
			 while (((str = in.readLine()) != null) && continue_linking) {
				 add_for_linking = true;
				 if (str.length() > 0) {
					 inputPrint(str);
				 }

				 currentline = new LinkerLine();
				 currentline.setString(str);

				 if (str.contains(":")) {
					 if (str.charAt(str.length()-1) == ':') {
						 // WARNING: OBJECT FILE RECORDS MAY NOT HAVE : AS THE LAST CHARACTER. THE ENDING : WILL BE IGNORED.
						 currentline.addError(68);
					 }
					 checkRecord(str.split(":"));
				 }
				 else {
					 // ERROR: OBJECT FILE RECORDS MUST CONTAIN COLONS TO SEPARATE FIELDS
					 currentline.addError(002);
					 add_for_linking = false;
				 }
				 
				 if (add_for_linking) {
					 OUTPUT_INTERMEDIATE.add(outputLine);
					 LOADER_INTERMEDIATE.add(currentline);
				 }
				 else {
					 OUTPUT_INTERMEDIATE.add(currentline);
				 }

			 }

			 printSymbolTable(SYMBOL_TABLE);

		 }catch (IOException e){

		 }

	 }

	 private static void runLinkerPassTwo () {
		 String overall_module_length = extendHex(Integer.toHexString(last_assigned_linker_address + previous_module_length - (int)Long.parseLong(first_module_load_point,16)), 4);

		 // Get date and time
		 Calendar cal = Calendar.getInstance();
		 String year = Integer.toString(cal.get(Calendar.YEAR));
		 String date = Integer.toString(cal.get(Calendar.DAY_OF_YEAR));    	
		 String hour = Integer.toString(cal.get(Calendar.HOUR_OF_DAY));

		 while(hour.length()<2){
			 hour = "0" + hour;
		 }
		 String min = Integer.toString(cal.get(Calendar.MINUTE));

		 while(min.length()<2){
			 min = "0" + min;
		 }
		 String sec = Integer.toString(cal.get(Calendar.SECOND));

		 while(sec.length()<2){
			 sec = "0" + sec;
		 }    	
		 String datetime = year + date + "," + hour + ":" + min + ":" + sec;

		 // Print load file header record
		 lfilePrint("H:" + LOAD_PROGRAM_NAME + ":" + first_module_load_point + ":" + first_module_execution + ":" + overall_module_length.toUpperCase() + ":" + datetime + ":FICKELL-LLM:0002:" + LOAD_PROGRAM_NAME);
		 loader_total_count++;

		 // Go through INTERMEDIATE and print text records
		 for (LinkerLine l : LOADER_INTERMEDIATE) {
			 String[] tokens = l.getString().split(":");

			 // Print first modules text records to the loader file
			 if (first_module) {
				 if (modify) {
					 if (tokens[0].equals("M")) {
						 int value = (int)Long.parseLong(modified_value, 16);
						 if (adjustments.equals("1")) {
							 String sign = tokens[3];
							 String label = tokens[4];
							 if (SYMBOL_TABLE.symbolIsDefined(label)) {
								 int adjustment_value = (int)Long.parseLong(SYMBOL_TABLE.getLinkerLocation(label), 16);
								 if (sign.equals("+")) {
									 value += adjustment_value;							 
								 }
								 else if (sign.equals("-")) {
									 value -= adjustment_value;						
								 }
								 String new_value_hex = extendHex(Integer.toHexString(value), 8);
								 int new_s_field = (int)Long.parseLong(new_value_hex.substring(5), 16);
								 if (new_s_field >= 0 && new_s_field <= 4095) {
									 lfilePrint("T:" + modified_address_hex + ":" + extendHex(Integer.toHexString(value), 8).toUpperCase() + ":" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
								 else {
									 // ERROR: NEW S-FIELD OUT OF RANGE
									 addPass2Error(63, previous_intermediate_string);
									 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
							 }
							 else {
								 // ERROR: ONE OR MORE LABELS USED FOR ADDRESS MODIFICATION ARE NOT DEFINED. THE TEXT RECORD THAT CALLS THIS MODIFICATION WILL BE REPLACED BY NOP
								 addPass2Error(65, previous_intermediate_string);
								 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
								 loader_t_count++;
								 loader_total_count++;
							 }
						 }
						 else if (adjustments.equals("2")) {
							 int i = 4;
							 int adjustment_value = 0;
							 boolean valid_labels = true;
							 boolean check = true;
							 while (i < 7 && check) {
								 if (SYMBOL_TABLE.symbolIsDefined(tokens[i])) {

									 adjustment_value = (int)Long.parseLong(SYMBOL_TABLE.getLinkerLocation(tokens[i]), 16);
									 if (tokens[i-1].equals("+")) {
										 value += adjustment_value;
									 }
									 else if (tokens[i-1].equals("-")) {
										 value -= adjustment_value;            					
									 }
									 i = i+2;
								 }
								 else {
									 valid_labels = false;
									 check = false;
								 }
							 }
							 if (valid_labels) {
								 String new_value_hex = extendHex(Integer.toHexString(value), 8);
								 int new_s_field = (int)Long.parseLong(new_value_hex.substring(5), 16);
								 if (new_s_field >= 0 && new_s_field <= 4095) {
									 lfilePrint("T:" + modified_address_hex + ":" + extendHex(Integer.toHexString(value), 8).toUpperCase() + ":" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
								 else {
									 // ERROR: NEW S-FIELD OUT OF RANGE
									 addPass2Error(63, previous_intermediate_string);
									 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
							 }
							 else {
								 // ERROR: ONE OR MORE LABELS USED FOR ADDRESS MODIFICATION ARE NOT DEFINED. THE TEXT RECORD THAT CALLS THIS MODIFICATION WILL BE REPLACED BY NOP
								 addPass2Error(65, previous_intermediate_string);
								 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
								 loader_t_count++;
								 loader_total_count++;					
							 }
						 }
						 else if (adjustments.equals("3")) {
							 int i = 4;
							 int adjustment_value = 0;
							 boolean valid_labels = true;
							 boolean check = true;
							 while (i < 9 && check) {
								 if (SYMBOL_TABLE.symbolIsDefined(tokens[i])) {

									 adjustment_value = (int)Long.parseLong(SYMBOL_TABLE.getLinkerLocation(tokens[i]), 16);
									 if (tokens[i-1].equals("+")) {
										 value += adjustment_value;
									 }
									 else if (tokens[i-1].equals("-")) {
										 value -= adjustment_value;            					
									 }
									 i = i+2;
								 }
								 else {
									 valid_labels = false;
									 check = false;
								 }
							 }

							 if (valid_labels) {
								 String new_value_hex = extendHex(Integer.toHexString(value), 8);
								 int new_s_field = (int)Long.parseLong(new_value_hex.substring(5), 16);
								 if (new_s_field >= 0 && new_s_field <= 4095) {
									 lfilePrint("T:" + modified_address_hex + ":" + extendHex(Integer.toHexString(value), 8).toUpperCase() + ":" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
								 else {
									 // ERROR: NEW S-FIELD OUT OF RANGE
									 addPass2Error(63, previous_intermediate_string);
									 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
							 }
							 else {
								 // ERROR: ONE OR MORE LABELS USED FOR ADDRESS MODIFICATION ARE NOT DEFINED. THE TEXT RECORD THAT CALLS THIS MODIFICATION WILL BE REPLACED BY NOP
								 addPass2Error(65, previous_intermediate_string);
								 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
								 loader_t_count++;
								 loader_total_count++;	
							 }
						 }
						 else if (adjustments.equals("4")) {
							 int i = 4;
							 int adjustment_value = 0;
							 boolean valid_labels = true;
							 boolean check = true;
							 while (i < 11 && check) {
								 if (SYMBOL_TABLE.symbolIsDefined(tokens[i])) {

									 adjustment_value = (int)Long.parseLong(SYMBOL_TABLE.getLinkerLocation(tokens[i]), 16);
									 if (tokens[i-1].equals("+")) {
										 value += adjustment_value;
									 }
									 else if (tokens[i-1].equals("-")) {
										 value -= adjustment_value;            					
									 }
									 i = i+2;
								 }
								 else {

								 }
							 }

							 if (valid_labels) {
								 String new_value_hex = extendHex(Integer.toHexString(value), 8);
								 int new_s_field = (int)Long.parseLong(new_value_hex.substring(5), 16);
								 if (new_s_field >= 0 && new_s_field <= 4095) {
									 lfilePrint("T:" + modified_address_hex + ":" + extendHex(Integer.toHexString(value), 8).toUpperCase() + ":" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
								 else {
									 // ERROR: NEW S-FIELD OUT OF RANGE
									 addPass2Error(63, previous_intermediate_string);
									 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
							 }
							 else {
								 // ERROR: ONE OR MORE LABELS USED FOR ADDRESS MODIFICATION ARE NOT DEFINED. THE TEXT RECORD THAT CALLS THIS MODIFICATION WILL BE REPLACED BY NOP
								 addPass2Error(65, previous_intermediate_string);
								 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
								 loader_t_count++;
								 loader_total_count++;	
							 }
						 }
						 modify = false;
						 modified_address_hex = "";
						 modified_value = "";
					 }
					 else {
						 // ERROR: TEXT RECORD WITH NEEDED ADJUSTMENTS DOES NOT HAVE A CORRESPONDING MOD RECORD, LINE WILL BE REPLACED BY NOP
						 addPass2Error(62, previous_intermediate_string);
						 lfilePrint("T:" + modified_address_hex + ":78000000:" + LOAD_PROGRAM_NAME);
					 }
				 }
				 else {
					 if (tokens[0].equals("T")) {
						 if (tokens[3].equals("A") || tokens[3].equals("R")) {
							 lfilePrint("T:" + tokens[1] + ":" + tokens[2] + ":" + LOAD_PROGRAM_NAME);
							 loader_t_count++;
							 loader_total_count++;
						 }
						 else if (tokens[3].equals("M")) {
							 modified_address_hex = tokens[1];
							 modified_value = tokens[2];
							 adjustments = tokens[4];
							 previous_intermediate_string = l.getString();
							 modify = true;
						 }
					 }
					 else if (tokens[0].equals("E")) {
						 first_module = false;
					 }
				 }
			 }

			 // Update and print text records for other modules
			 else {
				 if (modify) {
					 if (Integer.parseInt(modified_address_hex.substring(2), 16) <= 4095) {
						 if (tokens[0].equals("M")) {
							 int value = (int)Long.parseLong(modified_value, 16);
							 if (adjustments.equals("1")) {
								 String sign = tokens[3];
								 String label = tokens[4];
								 int adjustment_value = (int)Long.parseLong(SYMBOL_TABLE.getLinkerLocation(label), 16);
								 if (sign.equals("+")) {
									 value += adjustment_value;
								 }
								 else if (sign.equals("-")) {
									 value -= adjustment_value;
								 }
								 String new_value_hex = extendHex(Integer.toHexString(value), 8);
								 int new_s_field = (int)Long.parseLong(new_value_hex.substring(5), 16);

								 if (new_s_field >= 0 && new_s_field <= 4095) {
									 lfilePrint("T:" + modified_address_hex + ":" + extendHex(Integer.toHexString(value), 8).toUpperCase() + ":" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
								 else {
									 // ERROR: NEW S-FIELD OUT OF RANGE
									 addPass2Error(63, previous_intermediate_string);
									 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }

							 }
							 else if (adjustments.equals("2")) {
								 int i = 4;
								 int adjustment_value = 0;
								 while (i < 7) {
									 adjustment_value = (int)Long.parseLong(SYMBOL_TABLE.getLinkerLocation(tokens[i]), 16);
									 if (tokens[i-1].equals("+")) {
										 value += adjustment_value;
									 }
									 else if (tokens[i-1].equals("-")) {
										 value -= adjustment_value;            					
									 }
									 i = i+2;
								 }
								 String new_value_hex = extendHex(Integer.toHexString(value), 8);
								 int new_s_field = (int)Long.parseLong(new_value_hex.substring(5), 16);
								 if (new_s_field >= 0 && new_s_field <= 4095) {
									 lfilePrint("T:" + modified_address_hex + ":" + extendHex(Integer.toHexString(value), 8).toUpperCase() + ":" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
								 else {
									 // ERROR: NEW S-FIELD OUT OF RANGE
									 addPass2Error(63, previous_intermediate_string);
									 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }

							 }
							 else if (adjustments.equals("3")) {
								 int i = 4;
								 int adjustment_value = 0;
								 while (i < 9) {
									 adjustment_value = (int)Long.parseLong(SYMBOL_TABLE.getLinkerLocation(tokens[i]), 16);
									 if (tokens[i-1].equals("+")) {
										 value += adjustment_value;
									 }
									 else if (tokens[i-1].equals("-")) {
										 value -= adjustment_value;            					
									 }
									 i = i+2;
								 }
								 String new_value_hex = extendHex(Integer.toHexString(value), 8);
								 int new_s_field = (int)Long.parseLong(new_value_hex.substring(5), 16);
								 if (new_s_field >= 0 && new_s_field <= 4095) {
									 lfilePrint("T:" + modified_address_hex + ":" + extendHex(Integer.toHexString(value), 8).toUpperCase() + ":" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
								 else {
									 // ERROR: NEW S-FIELD OUT OF RANGE
									 addPass2Error(63, previous_intermediate_string);
									 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }

							 }
							 else if (adjustments.equals("4")) {
								 int i = 4;
								 int adjustment_value = 0;
								 while (i < 11) {
									 adjustment_value = (int)Long.parseLong(SYMBOL_TABLE.getLinkerLocation(tokens[i]), 16);
									 if (tokens[i-1].equals("+")) {
										 value += adjustment_value;
									 }
									 else if (tokens[i-1].equals("-")) {
										 value -= adjustment_value;            					
									 }
									 i = i+2;
								 }
								 String new_value_hex = extendHex(Integer.toHexString(value), 8);
								 int new_s_field = (int)Long.parseLong(new_value_hex.substring(5), 16);
								 if (new_s_field >= 0 && new_s_field <= 4095) {
									 lfilePrint("T:" + modified_address_hex + ":" + extendHex(Integer.toHexString(value), 8).toUpperCase() + ":" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
								 else {
									 // ERROR: NEW S-FIELD OUT OF RANGE
									 addPass2Error(63, previous_intermediate_string);
									 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }

							 }
							 modify = false;
							 modified_address_hex = "";
							 modified_value = "";
						 }
						 else {
							 // ERROR: TEXT RECORD WITH NEEDED MODIFICATIONS DOESNT HAVE CORESPONDING MOD RECORD
							 addPass2Error(62, previous_intermediate_string);
							 lfilePrint("T:" + modified_address_hex + ":78000000:" + PROGRAM_NAME);
						 }
					 }
					 else {
						 // ERROR: ADJUSTED LOAD ADDRESS IS OUT OF RANGE
						 addPass2Error(64, l.getString());
					 }
				 }
				 else {
					 if (tokens[0].equals("H")) {
						 PROGRAM_NAME = tokens[1];
						 address_adjustment = (int)Long.parseLong(SYMBOL_TABLE.getAdjustment(PROGRAM_NAME), 16);
						 System.out.println(address_adjustment);
					 }
					 else if (tokens[0].equals("T")) {
						 if (tokens[3].equals("A")) {
							 String location_hex = extendHex(Integer.toHexString((int)Long.parseLong(tokens[1],16) + address_adjustment), 6).toUpperCase();
							 if (Integer.parseInt(location_hex, 16) <= 4095) {
								 lfilePrint("T:" + location_hex + ":" + tokens[2] + ":" + PROGRAM_NAME);
								 loader_t_count++;
								 loader_total_count++;
							 }
							 else {
								 // ERROR: LOAD ADDRESS IS OUT OF PROGRAM RANGE
								 addPass2Error(64, l.getString());

							 }

						 }
						 else  if (tokens[3].equals("R")) {
							 String location_hex = extendHex(Integer.toHexString((int)Long.parseLong(tokens[2].substring(2),16) + address_adjustment), 6).toUpperCase();
							 String relocated_hex = extendHex(Integer.toHexString((int)Long.parseLong(tokens[2].substring(2), 16) + address_adjustment), 8).toUpperCase();
							 
							 if ((int)Long.parseLong(location_hex, 16) <= 4095) {
								 int new_s_field_int = (int)Long.parseLong(location_hex.substring(5), 16);
								 if (new_s_field_int >= 0 && new_s_field_int <= 4095) {
									 lfilePrint("T:" + location_hex + ":" + relocated_hex + ":" + PROGRAM_NAME);
									 loader_t_count++;
									 loader_total_count++;
								 }
								 else {
									 // ERROR: RELOCATED S-FIELD IS OUT OF RANGE, MUST BE IN RANGE OF 0-4095. LINE WILL BE REPLACED BY NOP
									 lfilePrint("T:" + location_hex + ":78000000:" + PROGRAM_NAME);
									 addPass2Error(63, l.getString());
									 loader_t_count++;
									 loader_total_count++;
								 }
							 }
							 else {
								 // ERROR: LOAD ADDRESS IS OUT OF PROGRAM RANGE
								 addPass2Error(64, l.getString());

							 }
							

						 }
						 else if (tokens[3].equals("M")) {
							 modified_address_hex = extendHex(Integer.toHexString((int)Long.parseLong(tokens[1], 16) + address_adjustment), 6).toUpperCase();
							 modified_value = tokens[2];
							 previous_intermediate_string = l.getString();
							 adjustments = tokens[4];
							 modify = true;
						 }
					 }
				 }
			 }

		 }

		 for(LinkerLine l : OUTPUT_INTERMEDIATE){
			 outputPrint(l.toString());
			 for (int i : l.getErrors()) {
				 outputPrint("ERROR " + i + ": " + eHandler.get(i));
			 }
		 }

		 // Print end record
		 loader_total_count++;
		 lfilePrint("E:" + extendHex(Integer.toHexString(loader_total_count), 4).toUpperCase() + ":" + extendHex(Integer.toHexString(loader_t_count), 4).toUpperCase() + ":" + LOAD_PROGRAM_NAME);
	 }


	 private static void checkRecord (String tokens[]) {
		 if (tokens[0].equals("H")) {
			 checkHeaderRecord(tokens);
			 totalrecords++;
		 }
		 else if (valid_header) {
			 if (tokens[0].equals("T")) {
				 checkTextRecord(tokens);
				 totalrecords++;
				 t_record_count++;
			 }
			 else if (tokens[0].equals("L")) {
				 checkLinkingRecord(tokens);
				 totalrecords++;
				 l_record_count++;
			 }
			 else if (tokens[0].equals("M")) {
				 checkModificationRecord(tokens);
				 totalrecords++;
				 m_record_count++;
			 }
			 else if (tokens[0].equals("E") ){
				 totalrecords++;
				 checkEndRecord(tokens);
				 totalrecords = 0;
				 t_record_count = 0;
				 l_record_count = 0;
				 m_record_count = 0;
			 }
			 else {
				 // ERROR: RECORD TYPE IS INVALID
				 add_for_linking = false;
				 currentline.addError(3);
			 }
		 }
		 else {
			 continue_linking = false;	
			 add_for_linking = false;
			 currentline.addError(66); 
		 }

	 }

	 private static void checkHeaderRecord (String[] tokens) {
		 if (tokens.length == 11) {
			 int assembler_load_page = 0;
			 int assembler_load_address = 0;
			 String program_name = tokens[1];
			 String fickell = tokens[9];
			 String prgm_name_2 = tokens[10]; // MUST MATCH tokens[1]
			 Boolean valid_start = true;
			 Boolean valid_module_length = true;
			 Boolean valid_execution_start = true;
			 Boolean valid_program_names = true;

			 
			 // Check for valid program name 
			 if (!isValidLabel(program_name)) {
				 // ERROR: PROGRAM NAME IS INVALID, MUST BE 2-32 CHARACTERS AND NUMBERS LONG AND MUST BEGIN WITH A LETTER
				 add_for_linking = false;
				 currentline.addError(51);
				 valid_start = false;
			 }

			 // Check for valid load page and address
			 if (tokens[2].length() == 6) {
				 if (isValidHex(tokens[2].substring(0,2))) {
					 assembler_load_page = (int)Long.parseLong(tokens[2].substring(0,2),16);
					 if (assembler_load_page < 0 || assembler_load_page > 3) {
						 // ERROR: ASSEMBLER ASSIGNED LOAD PAGE IS INVALID, MUST BE IN RANGE OF 0-3
						 add_for_linking = false;
						 currentline.addError(4);
						 valid_start = false;
					 }
				 }
				 else {
					 // ERROR: LOAD PAGE CONTAINS INVALID HEXADECIMAL SYNTAX
					 add_for_linking = false;
					 currentline.addError(5);
					 valid_start = false;
				 }

				 if (isValidHex(tokens[2].substring(2))) {
					 assembler_load_address = (int)Long.parseLong(tokens[2].substring(2),16);
					 if (assembler_load_address < 0 || assembler_load_address > 4095) {
						 // ERROR: ASSEMBLER ASSIGNED LOAD ADDRESS IS INVALID, MUST BE IN RANGE OF 0-4095
						 add_for_linking = false;
						 currentline.addError(6);
						 valid_start = false;
					 }
				 }
				 else {
					 // ERROR: LOAD ADDRESS CONTAINS INVALID HEXADECIMAL SYNTAX
					 add_for_linking = false;
					 currentline.addError(7);
					 valid_start = false;
				 }
			 }
			 else {
				 // ERROR: ADDRESS LENGTH MUST BE 6
				 add_for_linking = false;
				 currentline.addError(70);
				 valid_start = false;
			 }

			 // Check for valid module length
			 if (tokens[3].length() == 4) {
				 if (isValidHex(tokens[3])) {
					 int total_length = (int)Long.parseLong(tokens[3], 16);
					 CURRENT_MODULE_LENGTH = total_length;

					 if (total_length < 0 || total_length > 16383) {
						 // ERROR: INVALID MODULE LENGTH, MUST BE IN RANGE 0-16383
						 add_for_linking = false;
						 currentline.addError(8);
						 valid_module_length = false;

					 }
				 }
				 else {
					// ERROR: MODULE LENGTH CONTAINS INVALID HEXADECIMAL SYNTAX
					 add_for_linking = false;
					 currentline.addError(9);
					 valid_module_length = false;
				 }
			 }
			 else {
				// ERROR: MODULE LENGTH MUST BE 4 DIGITS LONG
				 add_for_linking = false;
				 currentline.addError(54);
				 valid_module_length = false;
				 
			 }

			 // Check for valid execution start address
			 if (tokens[4].length() == 4) {
				 if (isValidHex(tokens[4])) {

					 int execution_address = (int)Long.parseLong(tokens[4], 16);

					 if (execution_address < 0 || execution_address > 4095) {
						 // ERROR: EXECUTION START ADDRESS EXCEEDS THE PROGRAM LENGTH
						 add_for_linking = false;
						 currentline.addError(10);
						 valid_execution_start = false;
					 }

				 }
				 else {
					 // ERROR: EXECUTION START ADDRESS CONTAINS INVALID HEXADECIMAL SYNTAX
					 add_for_linking = false;

					 currentline.addError(11);
					 valid_execution_start = false;
				 }
			 }
			 else {
				 
				// ERROR: EXECUTION START ADDRESS MUST BE 4 DIGITS LONG
				 add_for_linking = false;
				 currentline.addError(55);
				 valid_execution_start = false;

			 }

			 // Check to make sure Program Names match up
			 if (!program_name.equals(prgm_name_2)) {
				 // ERROR: PROGRAM NAMES MUST MATCH IN THE HEADER RECORD OF AN OBJECT FILE
				 currentline.addError(12);
				 add_for_linking = false;
				 valid_program_names = false;
			 }

			 // Check for FICKELL to be the machine name
			 if (!fickell.equals("FICKELL-ASM")) {
				 // WARNING: OBJECT FILE SHOULD HAVE BEEN PRODUCED BY A FICKELL ASSEMBLER
				 currentline.addError(13);
			 }

			 // Check for duplicate entry
			 if (SYMBOL_TABLE.symbolIsDefined(program_name)) {
				 // ERROR: PROGRAM NAME HAS ALREADY BEEN DEFINED IN THE SYMBOL TABLE
				 currentline.addError(14);
				 add_for_linking = false;
				 valid_program_names = false;
			 }


			 // If valid, set correct info to variables and add program to symbol table
			 if (valid_start && valid_module_length && valid_execution_start && valid_program_names) {
				 int linker_address = last_assigned_linker_address + previous_module_length + assembler_load_address;
				 int relocation_value = linker_address - (int)Long.parseLong(tokens[2].substring(2),16);
				 String linker_address_hex = extendHex(Integer.toHexString(linker_address),4);
				 linker_address_hex = extendHex(Integer.toHexString(assembler_load_page), 2) + linker_address_hex;

				 // Add valid program name to symbol table
				 if (SYMBOL_TABLE.size() < 50) {
					 SYMBOL_TABLE.defineSymbol(new LinkerSymbol(program_name, tokens[2], tokens[2], linker_address_hex, tokens[3],Integer.toHexString(relocation_value), Integer.toHexString(execution_start)));
					 last_assigned_linker_address = linker_address;
					 previous_module_length = (int)Long.parseLong(tokens[3], 16);
				 }
				 else {
					 // ERROR: MAX NUMBER OF ENTRIES REACHED
					 add_for_linking = false;
					 currentline.addError(61);
				 }


				 // If this is the first header record, save program name for loader file header and add to intermediate
				 if (!valid_header) {
					 LOAD_PROGRAM_NAME = program_name;
					 valid_header = true;
					 first_module_load_point = linker_address_hex;
					 first_module_execution = extendHex(Integer.toHexString(execution_start), 4);
				 }
				 PROGRAM_NAME = program_name;
			 }
			 else if (!valid_header) {
				 // FATAL ERROR! : FIRST HEADER RECORD IN OBJECT FILE MUST BE VALID. LINKING/LOADING WILL BE STOPPED
				 continue_linking = false;	
				 add_for_linking = false;
				 currentline.addError(66);
			 }

		 }
		 else {
			 // ERROR: HEADER RECORDS MUST CONTAIN 11 FIELDS
			 currentline.addError(40);
			 add_for_linking = false;
			 
			 if (!valid_header) {
				 continue_linking = false;	
				 add_for_linking = false;
				 currentline.addError(66);
			 }
		 }
		 outputLine = currentline;
	 }

	 private static void checkTextRecord (String[] tokens) {
		 if (tokens.length == 6) {
			 String page_hex = "";
			 String location_hex = "";
			 String instruction_data_word = tokens[2];
			 String status_flag = tokens[3];
			 String adjustments = tokens[4];
			 String program_name = tokens[5];
			 boolean valid_location = true;
			 boolean valid_instruction_data_word = true;
			 boolean valid_status_flag = true;
			 boolean valid_adjustments = true;
			 boolean valid_program_name = true;

			 // Check instruction location 
			 if (tokens[1].length() == 6) {
				 page_hex = tokens[1].substring(0,2);
				 location_hex = tokens[1].substring(2);
				 if (isValidHex(page_hex)) {
					 int page_int = (int)Long.parseLong(page_hex, 16);
					 if (page_int < 0 || page_int > 3) {
						 // ERROR: PROGRAM ASSIGNED PAGE VALUE IS INVALID, MUST BE IN RANGE OF 0-3
						 add_for_linking = false;
						 currentline.addError(15);
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: PROGRAM ASSIGNED PAGE VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 add_for_linking = false;
					 currentline.addError(16);
					 valid_location = false;
				 }

				 if (isValidHex(location_hex)) {
					 int loc_int = (int)Long.parseLong(location_hex, 16);
					 if (loc_int < 0 || loc_int > 4095) {
						 // ERROR: PROGRAM ASSIGNED LOCATION VALUE IS INVALID, MUST BE IN RANGE OF 0-16383
						 add_for_linking = false;
						 currentline.addError(17);
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: PROGRAM ASSIGNED LOCATION VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 add_for_linking = false;
					 currentline.addError(18);
					 valid_location = false;
				 }
			 }
			 else {
				 // ERROR: PROGRAM ASSIGNED LOCATION MUST BE 6 HEX DIGITS LONG
				 add_for_linking = false;
				 currentline.addError(67);
				 valid_location = false;

			 }
			 
			 // Check instruction/data word syntax
			 if (instruction_data_word.length() == 8) {
				 if (!isValidHex(instruction_data_word)) {
					 // ERROR: INSTRUCTION/DATA WORD CONTAINS INVALID HEXADECIMAL SYNTAX
					 currentline.addError(19);
					 valid_instruction_data_word = false;
				 }
			 }
			 else {
				// ERROR: INSTRUCTION/DATA WORD MUST BE 8 DIGITS LONG
				 currentline.addError(53);
				 valid_instruction_data_word = false;
			 }

			 // Check status flag syntax
			 if (!status_flag.equals("A") && !status_flag.equals("R") && !status_flag.equals("E") && !status_flag.equals("M")) {
				 // ERROR: INVALID STATUS FLAG USED
				 currentline.addError(20);
				 valid_status_flag = false;
			 }

			 // Check number of adjustments
			 if (adjustments.length() == 1) {
				 if (isValidHex(adjustments)) {
					 if ((int)Long.parseLong(adjustments, 16) < 0 || (int)Long.parseLong(adjustments, 16) > 4) {
						 // ERROR: INVALID NUMBER OF M ADJUSTMENTS USED
						 currentline.addError(21);
						 valid_adjustments = false;
					 }
				 }
				 else {
					// ERROR: THE VALUE OF ADJUSTMENTS REQUIRED MUST BE 1 HEX DIGIT IN LENGTH
					 currentline.addError(71);
					 valid_adjustments = false;
				 }
			 }
			 else {
				 // ERROR: THE VALUE OF ADJUSTMENTS REQUIRED MUST BE 1 HEX DIGIT IN LENGTH
				 currentline.addError(69);
				 valid_adjustments = false;
			 }
			 
			 // Check program name
			 if (!program_name.equals(PROGRAM_NAME)) {
				 // ERROR: TEXT RECORD PROGRAM NAME MUST MATCH CURRENT MODULES PROGRAM NAME
				 currentline.addError(22);
				 valid_program_name = false;
			 }

			 // IF VALID TEXT RECORD ADD TO INTERMEDIATE
			 if (add_for_linking) {
				 if (!valid_instruction_data_word || !valid_status_flag || !valid_adjustments || !valid_program_name) {
					 outputLine = new LinkerLine();
					 outputLine.setString(currentline.getString());		
					 int current_errors = Integer.valueOf(currentline.getErrors().toString().substring(1,3));
					 outputLine.addError(current_errors);
					 currentline.setString("T:" + tokens[1] + ":78000000:A:0:" + PROGRAM_NAME);
				 }
				 else {
					 outputLine = currentline;
				 }
				
			 }
		 }
		 else {
			 // ERROR: TEXT RECORDS MUST CONTAIN EXACTLY 6 FIELDS
			 add_for_linking = false;
			 currentline.addError(23);
		 }
	 }

	 private static void checkLinkingRecord (String[] tokens) {
		 if (tokens.length == 4) {
			 String entry_name = tokens[1];
			 String page_hex = "";
			 String location_hex = "";
			 String program_name = tokens[3];
			 boolean valid_entry_name = true;
			 boolean valid_location = true;
			 boolean valid_program_name = true;

			 // Check entry name syntax
			 if (entry_name.length() > 1) {
				 if (!isValidLabel(entry_name)) {
					 // ERROR: LABEL HAS INVALID SYNTAX
					 add_for_linking = false;
					 currentline.addError(24);
					 valid_entry_name = false;
				 }
			 }
			 else {
				 // ERROR: LABEL HAS INVALID SYNTAX
				 add_for_linking = false;
				 currentline.addError(24);
				 valid_entry_name = false; 
			 }

			 if (SYMBOL_TABLE.symbolIsDefined(entry_name)) {
				 // ERROR: ENTRY NAME IS ALREADY DEFINED IN THE SYMBOL TABLE
				 add_for_linking = false;
				 currentline.addError(25);
				 valid_entry_name = false;
			 }
			 
			 // Check page and location syntax
			 if (tokens[2].length() == 6) {
				 if (isValidHex(tokens[2].substring(0,2))) {
					 page_hex = tokens[2].substring(0,2);
					 int page_int = (int)Long.parseLong(page_hex, 16);

					 if (page_int < 0 || page_int > 3) {
						 // ERROR: PAGE VALUE IS INVALID, MUST BE IN THE RANGE OF 0-3
						 currentline.addError(26);
						 add_for_linking = false;
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: PAGE VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 add_for_linking = false;
					 valid_location = false;
					 currentline.addError(27);
				 }

				 if (isValidHex(tokens[2].substring(2))) {
					 location_hex = tokens[2].substring(2);
					 int loc_int = (int)Long.parseLong(location_hex, 16);

					 if (loc_int < 0 || loc_int > 4095) {
						 // ERROR: LOCATION VALUE IS INVALID, MUST BE IN RANGE OF 0-16383
						 add_for_linking = false;
						 currentline.addError(28);
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: LOCATION VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 add_for_linking = false;
					 valid_location = false;
					 currentline.addError(29);
				 }
			 }
			 else {
				 // ERROR: LOCATION HEX MUST BE 6 DIGITS LONG
				 add_for_linking = false;
				 currentline.addError(52);
				 valid_location = false;
			 }

			 // Check program name syntax
			 if (!program_name.equals(PROGRAM_NAME)) {
				 // ERROR: LINKING RECORD PROGRAM NAME MUST MATCH CURRENT MODULE'S PROGRAM NAME
				 add_for_linking = false;
				 currentline.addError(30);
				 valid_program_name = false;
			 }

			 // IF VALID LINKING RECORD UPDATE VALUES AND UPDATE SYMBOL TABLE
			 if (valid_entry_name && valid_location && valid_program_name) {
				 outputLine = currentline;
				 int linker_address = (int)Long.parseLong(location_hex, 16) + last_assigned_linker_address;
				 int adjustment = linker_address - (int)Long.parseLong(location_hex, 16);
				 String linker_address_hex = extendHex(Integer.toHexString(linker_address),4);
				 linker_address_hex = page_hex + linker_address_hex;
				if (SYMBOL_TABLE.size() < 50) {
				 SYMBOL_TABLE.defineSymbol(new LinkerSymbol(entry_name, "n/a", tokens[2], linker_address_hex, "1", "n/a", "n/a"));   
				}
				else {
					// ERROR: THERE ARE TOO MANY ENTRIES IN THE SYMBOL TABLE, ENTRY WILL BE IGNORED
					 add_for_linking = false;
					currentline.addError(61);
				}
			 }


		 }
		 else {
			 // ERROR: LINKING RECORDS MUST CONTAIN EXACTLY 4 FIELDS
			 add_for_linking = false;
			 currentline.addError(31);
		 }
	 }

	 private static void checkModificationRecord (String[] tokens) {
		 boolean valid_location = true;
		 boolean valid_adjustments = true;
		 boolean valid_signs = true;
		 boolean valid_labels = true;
		 boolean valid_program_name = true;

		 if (tokens.length == 6) {
			 String page_hex = "";
			 String location_hex = "";
			 String adjustments = tokens[2];
			 String sign = tokens[3];
			 String label_name = tokens[4];
			 String program_name = tokens[5];

			 // Check location 
			 if (tokens[1].length() == 6) {
				 if (isValidHex(tokens[1].substring(0,2))) {
					 page_hex = tokens[1].substring(0,2);
					 int page_int = (int)Long.parseLong(page_hex, 16);
					 if (page_int < 0 || page_int > 3) {
						 // ERROR: PAGE VALUE IS INVALID, MUST BE IN RANGE OF 0-3
						 add_for_linking = false;
						 currentline.addError(32);
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: PAGE VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 add_for_linking = false;
					 currentline.addError(33);
					 valid_location = false;

				 }

				 if (isValidHex(tokens[1].substring(2))) {
					 location_hex = tokens[1].substring(2);
					 int loc_int = (int)Long.parseLong(location_hex, 16);
					 if (loc_int < 0 || loc_int > 4095) {
						 // ERROR: LOCATION VALUE IS INVALID, MUST BE IN RANGE OF 0-4095
						 add_for_linking = false;
						 currentline.addError(34);
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: LOCATION VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 currentline.addError(35);
					 add_for_linking = false;
					 valid_location = false;
				 }
			 }
			 else {
				 // ERROR: LOCATION HEX MUST BE 6 DIGITS
				 currentline.addError(52);
				 add_for_linking = false;
				 valid_location = false;
			 }

			 // Check for valid adjustment value
			 if (adjustments.length() == 1) {
				 if (!adjustments.equals("1")) {
					 // ERROR: THE NUMBER OF ADJUSTMENTS DOES NOT MATCH THE ADJUSTMENT VALUE 
					 currentline.addError(36);
					 add_for_linking = false;
					 valid_adjustments = false;
				 }
			 }
			 else {
				 // ERROR: ADJUSTMENT VALUE MUST BE ONLY 1 HEX DIGIT IN LENGTH
				 currentline.addError(69);
				 add_for_linking = false;
				 valid_adjustments = false;
			 }

			 // Check for valid sign 
			 if (!sign.equals("+") && !sign.equals("-")) {
				 // ERROR: INVALID SIGN USED IN THE ADJUSTMENT SET
				 currentline.addError(37);
				 add_for_linking = false;
				 valid_signs = false;
			 }

			 // Check for valid label name
			 if (label_name.length() > 1) {
				 if (!isValidLabel(label_name)) {
					 // ERROR: LABEL NAME IN THE ADJUSTMENT SET IS INVALID
					 currentline.addError(38);
					 add_for_linking = false;
					 valid_labels = false;
				 }
			 }
			 else {
				// ERROR: LABEL NAME IN THE ADJUSTMENT SET IS INVALID
				 currentline.addError(38);
				 add_for_linking = false;
				 valid_labels = false;
			 }

			 // Check for matching program name
			 if (!program_name.equals(PROGRAM_NAME)) {
				 // ERROR: MODIFICATION RECORD PROGRAM NAME MUST MATCH THE CURRENT MODULE'S PROGRAM NAME
				 currentline.addError(39);
				 add_for_linking = false;
				 valid_program_name = false;
			 }


		 }
		 else if (tokens.length == 8) {
			 String page_hex = "";
			 String location_hex = "";
			 String adjustments = tokens[2];
			 String program_name = tokens[7];
			 int sign_count = 3;
			 int label_count = 4;
			 int i = 0;


			 if (tokens[1].length() == 6) {
				 // Check location 
				 if (isValidHex(tokens[1].substring(0,2))) {
					 page_hex = tokens[1].substring(0,2);
					 int page_int = (int)Long.parseLong(page_hex, 16);
					 if (page_int < 0 || page_int > 3) {
						 // ERROR: PAGE VALUE IS INVALID, MUST BE IN RANGE OF 0-3
						 currentline.addError(32);
						 add_for_linking = false;
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: PAGE VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 currentline.addError(33);
					 add_for_linking = false;
					 valid_location = false;

				 }

				 if (isValidHex(tokens[1].substring(2))) {
					 location_hex = tokens[1].substring(2);
					 int loc_int = (int)Long.parseLong(location_hex, 16);
					 if (loc_int < 0 || loc_int > 4095) {
						 // ERROR: LOCATION VALUE IS INVALID, MUST BE IN RANGE OF 0-4095
						 currentline.addError(34);
						 add_for_linking = false;
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: LOCATION VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 currentline.addError(35);
					 add_for_linking = false;
					 valid_location = false;
				 }
			 }
			 else {
				 // ERROR: LOCATION HEX MUST BE 6 DIGITS
				 currentline.addError(52);
				 add_for_linking = false;
				 valid_location = false;
			 }

			 // Check for valid adjustment value
			 if (adjustments.length() == 1) {
			 if (!adjustments.equals("2")) {
				 // ERROR: THE NUMBER OF ADJUSTMENTS DOES NOT MATCH THE ADJUSTMENT VALUE 
				 currentline.addError(36);
				 add_for_linking = false;
				 valid_adjustments = false;
			 }
			 }
			 else {
				 currentline.addError(69);
				 add_for_linking = false;
				 valid_adjustments = false;
			 }

			 // Check for valid adjustment signs
			 while (i < 2) {
				 if (!tokens[sign_count].equals("+") && !tokens[sign_count].equals("-")) {
					 // ERROR: INVALID SIGN USED IN ADJUSTMENT SET
					 currentline.addError(37);
					 add_for_linking = false;
					 valid_signs = false;
				 }
				 i++;
				 sign_count = sign_count + 2;
			 }

			 // Check for valid adjustment labels
			 i = 0;
			 while (i < 2) {
				 if (tokens[label_count].length() > 1) {
					 if (!isValidLabel(tokens[label_count])) {
						 // ERROR: LABEL NAME IN THE ADJUSTMENT SET IS INVALID
						 currentline.addError(38);
						 add_for_linking = false;
						 valid_labels = false;
					 }
				 }
				 else {
					 // ERROR: LABEL NAME IN THE ADJUSTMENT SET IS INVALID
					 currentline.addError(38);
					 add_for_linking = false;
					 valid_labels = false;
				 }
				 i++;
				 label_count = label_count + 2;
			 }

			 // Check for matching program name
			 if (!program_name.equals(PROGRAM_NAME)) {
				 // ERROR: MODIFICATION RECORD PROGRAM NAME MUST MATCH THE CURRENT MODULE'S PROGRAM NAME
				 currentline.addError(39);
				 add_for_linking = false;
				 valid_program_name = false;
			 }

		 }
		 else if (tokens.length == 10) {
			 String page_hex = "";
			 String location_hex = "";
			 String adjustments = tokens[2];
			 String program_name = tokens[9];
			 int sign_count = 3;
			 int label_count = 4;
			 int i = 0;


			 if (tokens[1].length() == 6) {
				 // Check location 
				 if (isValidHex(tokens[1].substring(0,2))) {
					 page_hex = tokens[1].substring(0,2);
					 int page_int = (int)Long.parseLong(page_hex, 16);
					 if (page_int < 0 || page_int > 3) {
						 // ERROR: PAGE VALUE IS INVALID, MUST BE IN RANGE OF 0-3
						 currentline.addError(32);
						 add_for_linking = false;
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: PAGE VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 currentline.addError(33);
					 add_for_linking = false;
					 valid_location = false;

				 }

				 if (isValidHex(tokens[1].substring(2))) {
					 location_hex = tokens[1].substring(2);
					 int loc_int = (int)Long.parseLong(location_hex, 16);
					 if (loc_int < 0 || loc_int > 4095) {
						 // ERROR: LOCATION VALUE IS INVALID, MUST BE IN RANGE OF 0-4095
						 currentline.addError(34);
						 add_for_linking = false;
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: LOCATION VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 currentline.addError(35);
					 add_for_linking = false;
					 valid_location = false;
				 }
			 }
			 else {
				 // ERROR: LOCATION HEX MUST BE 6 DIGITS
				 currentline.addError(52);
				 add_for_linking = false;
				 valid_location = false;
			 }

			 // Check for valid adjustment value
			 if (adjustments.length() == 1) {
				 if (!adjustments.equals("3")) {
					 // ERROR: THE NUMBER OF ADJUSTMENTS DOES NOT MATCH THE ADJUSTMENT VALUE 
					 currentline.addError(36);
					 add_for_linking = false;
					 valid_adjustments = false;
				 }
			 }
			 else {
				 currentline.addError(69);
				 add_for_linking = false;
				 valid_adjustments = false;
			 }

			 // Check for valid adjustment signs
			 while (i < 3) {
				 if (!tokens[sign_count].equals("+") && !tokens[sign_count].equals("-")) {
					 // ERROR: INVALID SIGN USED IN ADJUSTMENT SET
					 currentline.addError(37);
					 add_for_linking = false;
					 valid_signs = false;
				 }
				 i++;
				 sign_count = sign_count + 2;
			 }

			 // Check for valid adjustment labels
			 i = 0;
			 while (i < 3) {
				 if (tokens[label_count].length() > 1) {
					 if (!isValidLabel(tokens[label_count])) {
						 // ERROR: LABEL NAME IN THE ADJUSTMENT SET IS INVALID
						 currentline.addError(38);
						 add_for_linking = false;
						 valid_labels = false;
					 }
				 }
				 else {
					// ERROR: LABEL NAME IN THE ADJUSTMENT SET IS INVALID
					 currentline.addError(38);
					 add_for_linking = false;
					 valid_labels = false;
				 }
				 i++;
				 label_count = label_count + 2;
			 }

			 // Check for matching program name
			 if (!program_name.equals(PROGRAM_NAME)) {
				 // ERROR: MODIFICATION RECORD PROGRAM NAME MUST MATCH THE CURRENT MODULE'S PROGRAM NAME
				 currentline.addError(39);
				 add_for_linking = false;
				 valid_program_name = false;
			 }

		 }
		 else if (tokens.length == 12) {
			 String page_hex = "";
			 String location_hex = "";
			 String adjustments = tokens[2];
			 String program_name = tokens[11];
			 int sign_count = 3;
			 int label_count = 4;
			 int i = 0;


			 if (tokens[1].length() == 6) {
				 // Check location 
				 if (isValidHex(tokens[1].substring(0,2))) {
					 page_hex = tokens[1].substring(0,2);
					 int page_int = (int)Long.parseLong(page_hex, 16);
					 if (page_int < 0 || page_int > 3) {
						 // ERROR: PAGE VALUE IS INVALID, MUST BE IN RANGE OF 0-3
						 currentline.addError(32);
						 add_for_linking = false;
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: PAGE VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 currentline.addError(33);
					 add_for_linking = false;
					 valid_location = false;

				 }

				 if (isValidHex(tokens[1].substring(2))) {
					 location_hex = tokens[1].substring(2);
					 int loc_int = (int)Long.parseLong(location_hex, 16);
					 if (loc_int < 0 || loc_int > 4095) {
						 // ERROR: LOCATION VALUE IS INVALID, MUST BE IN RANGE OF 0-4095
						 currentline.addError(34);
						 add_for_linking = false;
						 valid_location = false;
					 }
				 }
				 else {
					 // ERROR: LOCATION VALUE CONTAINS INVALID HEXADECIMAL SYNTAX
					 currentline.addError(35);
					 add_for_linking = false;
					 valid_location = false;
				 }
			 }
			 else {
				 // ERROR: LOCATION HEX MUST BE 6 DIGITS
				 currentline.addError(52);
				 add_for_linking = false;
				 valid_location = false;
			 }

			 // Check for valid adjustment value
			 if (adjustments.length() == 1) {
				 if (!adjustments.equals("4")) {
					 // ERROR: THE NUMBER OF ADJUSTMENTS DOES NOT MATCH THE ADJUSTMENT VALUE 
					 currentline.addError(36);
					 add_for_linking = false;
					 valid_adjustments = false;
				 }
			 }
			 else {
				 currentline.addError(69);
				 add_for_linking = false;
				 valid_adjustments = false;
			 }

			 // Check for valid adjustment signs
			 while (i < 4) {
				 if (!tokens[sign_count].equals("+") && !tokens[sign_count].equals("-")) {
					 // ERROR: INVALID SIGN USED IN ADJUSTMENT SET
					 currentline.addError(37);
					 add_for_linking = false;
					 valid_signs = false;
				 }
				 i++;
				 sign_count = sign_count + 2;
			 }

			 // Check for valid adjustment labels
			 i = 0;
			 while (i < 4) {
				 if (tokens[label_count].length() > 1) {
					 if (!isValidLabel(tokens[label_count])) {
						 // ERROR: LABEL NAME IN THE ADJUSTMENT SET IS INVALID
						 currentline.addError(38);
						 add_for_linking = false;
						 valid_labels = false;
					 }
				 }
				 else {
					// ERROR: LABEL NAME IN THE ADJUSTMENT SET IS INVALID
					 currentline.addError(38);
					 add_for_linking = false;
					 valid_labels = false;
				 }
				 i++;
				 label_count = label_count + 2;
			 }

			 // Check for matching program name
			 if (!program_name.equals(PROGRAM_NAME)) {
				 // ERROR: MODIFICATION RECORD PROGRAM NAME MUST MATCH THE CURRENT MODULE'S PROGRAM NAME
				 currentline.addError(39);
				 add_for_linking = false;
				 valid_program_name = false;
			 }
		 }
		 else {
			 // ERROR: MODIFICATION RECORDS MUST HAVE EXACTLY EITHER 6, 8, 10 OR 12 RECORDS
			 currentline.addError(41);
			 add_for_linking = false;
			 valid_program_name = false;
		 }
		 
		 if (add_for_linking) {
			 outputLine = currentline;
		 }
		 
		
	 }

	 private static void checkEndRecord (String[] tokens) {
		 if (tokens.length == 6) {
			 String total_records_hex = tokens[1];
			 String total_linking_records_hex = tokens[2];
			 String total_text_records_hex = tokens[3];
			 String total_modification_records_hex = tokens[4];
			 String program_name = tokens[5];
			 int total_int = 0;
			 int t_count_int = 0;
			 int l_count_int = 0;
			 int m_count_int = 0;
			 boolean valid_total = true;
			 boolean valid_t_count = true;
			 boolean valid_l_count = true;
			 boolean valid_m_count = true;
			 boolean valid_program_name = true;

			 // Check total record count
			 if (isValidHex(total_records_hex)) {
				 if (total_records_hex.length() == 4) {
					 total_int = (int)Long.parseLong(total_records_hex, 16);
					 if (total_int != totalrecords) {
						 // ERROR: THE VALUE OF TOTAL RECORDS IN THE END RECORD DOES NOT MATCH THE TOTAL RECORD COUNT
						 currentline.addError(42);
						 valid_total = false;
					 }
				 }
				 else {
					 // ERROR: TOTAL RECORDS LENGTH MUST EQUAL 4
					 currentline.addError(56);
					 valid_total = false;
				 }
			 }
			 else {
				 // ERROR: THE VALUE OF TOTAL RECORDS CONTAINS INVALID HEXADECIMAL SYNTAX
				 currentline.addError(43);
				 valid_total = false;
			 }

			 // Check text record count
			 if (isValidHex(total_text_records_hex)) {
				 if (total_text_records_hex.length() == 4) {
					 t_count_int = (int)Long.parseLong(total_text_records_hex, 16);
					 if (t_count_int != t_record_count) {
						 // ERROR: THE VALUE OF TOTAL TEXT RECORDS IN THE END RECORD DOES NOT MATCH THE TEXT RECORD COUNT
						 currentline.addError(44);
						 valid_t_count = false;
					 }
					 else if (t_record_count > CURRENT_MODULE_LENGTH) {
						 // ERROR: NUMBER OF TEXT RECORDS EXCEED THE CURRENT MODULES LENGTH
						 currentline.addError(60);
					 }
				 }
				 else {
					 // ERROR: TOTAL TEXT RECORDS LENGTH MUST EQUAL 4
					 currentline.addError(58);
					 valid_t_count = false;
				 }
			 }
			 else {
				 // ERROR: THE VALUE OF TOTAL TEXT RECORDS CONTAINS INVALID HEXADECIMAL SYNTAX
				 currentline.addError(45);
				 valid_t_count = false;
			 }

			 // Check modification record count
			 if (isValidHex(total_modification_records_hex)) {
				 if (total_modification_records_hex.length() == 4) {
					 m_count_int = (int)Long.parseLong(total_modification_records_hex, 16);
					 if (m_count_int != m_record_count) {
						 // ERROR: THE VALUE OF TOTAL MODIFICATION RECORDS IN THE END RECORD DOES NOT MATCH THE TEXT RECORD COUNT
						 currentline.addError(46);
						 valid_m_count = false;
					 }
				 }
				 else {
					 // ERROR: TOTAL MOD RECORDS LENGTH MUST EQUAL 4
					 currentline.addError(59);
					 valid_m_count = false;
				 }
			 }
			 else {
				 // ERROR: THE VALUE OF TOTAL MODIFICATION RECORDS CONTAINS INVALID HEXADECIMAL SYNTAX
				 currentline.addError(47);
				 valid_m_count = false;
			 }

			 // Check linking record count
			 if (isValidHex(total_linking_records_hex)) {
				 if (total_linking_records_hex.length() == 4) {
					 l_count_int = (int)Long.parseLong(total_linking_records_hex, 16);
					 if (l_count_int != l_record_count) {
						 // ERROR: THE VALUE OF TOTAL LINKING RECORDS IN THE END RECORD DOES NOT MATCH THE TEXT RECORD COUNT
						 currentline.addError(48);
						 valid_l_count = false;
					 }
				 }
				 else {
					 // ERROR: TOTAL LINKING RECORDS LENGTH MUST EQUAL 4
					 currentline.addError(57);
					 valid_l_count = false;
				 }
			 }
			 else {
				 // ERROR: THE VALUE OF TOTAL LINKING RECORDS CONTAINS INVALID HEXADECIMAL SYNTAX
				 currentline.addError(49);
				 valid_l_count = false;
			 }

		 }
		 else {
			 // ERROR: END RECORDS MUST HAVE EXACTLY 6 FIELDS
			 currentline.addError(50);
		 }
		 outputLine = currentline;

	 }

	 private static boolean isValidLabel (String label) {
		 boolean check = true, valid_characters = false, valid_length = true, valid_lead_ch = true, answer = false;
		 char[] characters = label.toCharArray();
		 int i = 0;

		 // Check leading character
		 if (!(Character.isLowerCase(characters[0])) && !(Character.isUpperCase(characters[0]))) {
			 // ERROR: LABEL MUST BEGIN WITH A CHARACTER
			 valid_lead_ch = false;
		 }
		 // Check each character of label
		 while (i < label.length() && check) {
			 if (Character.isLowerCase(characters[i]) || Character.isUpperCase(characters[i]) || Character.isDigit(characters[i])) {   
				 // 
				 valid_characters = true;
			 }
			 else {
				 check = false;
				 valid_characters = false;
			 }
			 i++;
		 }
		 // Check length of label
		 if (!(label.length() <= 32) && !(label.length() > 2)) {
			 valid_length = false;
		 }  

		 // Set valid_symbol to true to update to table
		 if (valid_lead_ch && valid_characters && valid_length) {
			 answer = true;
		 }
		 return answer;
	 }

	 private static Boolean isValidHex (String hex) {
		 boolean answer = false;

		 if (hex.matches("[A-F0-9]+")) {
			 answer = true;
		 }
		 else {
			 answer = false;
		 }
		 return answer;

	 }

	 private static Boolean isValidInput (String input) {
		 if (input.matches("[a-fA-F0-9:,+-]+")) {
			 return true;
		 }
		 else {
			 return false;
		 }
	 }

	 private static String extendHex (String hex, int digits) {
		 while (hex.length() < digits) {
			 hex = "0" + hex;
		 }
		 return hex;
	 }

	 private static void addPass2Error (int errorcode, String errorline_string) {
		 for (LinkerLine l : OUTPUT_INTERMEDIATE) {
			 if (l.getString().equals(errorline_string)) {
				 l.addError(errorcode);
			 }
		 }
	 }
	 public static void inputPrint(String str){
		 AssemblerFrame.getInputArea().append(str + newline);
	 }

	 public static void outputPrint(String str){
		 AssemblerFrame.getOutputArea().append(str + newline);
	 }

	 public static void symbolPrint(String str){
		 AssemblerFrame.getSymbolArea().append(str + newline);
	 }

	 public static void lfilePrint(String str){
		 AssemblerFrame.getLfileArea().append(str + newline);
	 }	

	 public static void printSymbolTable(LinkerSymbolTable table){

		 symbolPrint("Symbol\tLoadAdr\tAsmAdr\tLinkAdr\tLen\tAdjust\tExStart");
		 for (LinkerSymbol symbol : table){
			 symbolPrint(symbol.toString());
		 }
	 }
}
